#pragma once
#include "MaterialRenderProxy.h"
#include "MeshBatch.h"
#include "PrimitiveSceneProxy.h"
#include "RenderingPrefix.h"
#include "RenderResource.h"
#include "Shader.h"
#include "Resource/VertexAttributes.h"
#include "Runtime/CGI/PipelineStateCache.h"
#include "Runtime/Misc/TypeHash.h"
#include "Runtime/Misc/hash/CityHash.h"

namespace Alice
{
	class ViewInfo;
	/** 
	 * Number of resource bindings to allocate inline within a FMeshDrawCommand.
	 * This is tweaked so that the bindings for BasePass shaders of an average material using a FLocalVertexFactory fit into the inline storage.
	 * Overflow of the inline storage will cause a heap allocation per draw (and corresponding cache miss on traversal)
	 */
	const int32 NumInlineShaderBindings = 10;
	class Scene;
	/** FVisibleMeshDrawCommand sort key. */
	class FMeshDrawCommandSortKey
	{
	public:
		union 
		{
			uint64 PackedData;

			struct
			{
				uint64 VertexShaderHash		: 16; // Order by vertex shader's hash.
				uint64 PixelShaderHash		: 32; // Order by pixel shader's hash.
				uint64 Background			: 1;
				uint64 Masked				: 15; // First order by masked.
			} BasePass;

			struct
			{
				uint64 MeshIdInPrimitive	: 16; // Order meshes belonging to the same primitive by a stable id.
				uint64 Distance				: 32; // Order by distance.
				uint64 Priority				: 16; // First order by priority.
			} Translucent;

			struct 
			{
				uint64 VertexShaderHash : 32;	// Order by vertex shader's hash.
				uint64 PixelShaderHash : 32;	// First order by pixel shader's hash.
			} Generic;
		};

		FORCEINLINE bool operator!=(FMeshDrawCommandSortKey B) const
		{
			return PackedData != B.PackedData;
		}

		FORCEINLINE bool operator<(FMeshDrawCommandSortKey B) const
		{
			return PackedData < B.PackedData;
		}

		static const FMeshDrawCommandSortKey Default;
	};
	/** Flags that may be passed along with a visible mesh draw command OR stored with a cached one. */
	enum class EFVisibleMeshDrawCommandFlags : uint8
	{
		Default = 0U,
		/** If set, the FMaterial::MaterialUsesWorldPositionOffset_RenderThread() indicates that WPO is active for the given material. */
		MaterialUsesWorldPositionOffset = 1U << 0U,

		/**
		 * If set, the FMaterial::ShouldAlwaysEvaluateWorldPositionOffset() indicates that WPO is ALWAYS active for the given material.
		 * NOTE: This flag is only set if MaterialUsesWorldPositionOffset is also set.
		 */
		MaterialAlwaysEvaluatesWorldPositionOffset = 1U << 1U,

		/** If set, the mesh draw command supports primitive ID steam (required for dynamic instancing and GPU-Scene instance culling). */
		HasPrimitiveIdStreamIndex = 1U << 2U,

		/** If set, forces individual instances to always be culled independently from the primitive */
		ForceInstanceCulling = 1U << 3U,

		/** If set, requires that instances preserve their original draw order in the draw command */
		PreserveInstanceOrder = 1U << 4U,

		/** If set, the instance culling machinery will pull the instance count from the primitive in FScene, making it possible to bypass MDC re-caching */
		FetchInstanceCountFromScene = 1U << 5U,

		All = MaterialUsesWorldPositionOffset | HasPrimitiveIdStreamIndex | ForceInstanceCulling | PreserveInstanceOrder | FetchInstanceCountFromScene,
		NumBits = 6U
	};
	ENUM_CLASS_FLAGS(EFVisibleMeshDrawCommandFlags);
	static_assert(uint32(EFVisibleMeshDrawCommandFlags::All) < (1U << uint32(EFVisibleMeshDrawCommandFlags::NumBits)), "EFVisibleMeshDrawCommandFlags::NumBits too small to represent all flags in EFVisibleMeshDrawCommandFlags.");
	/**
	 * Wrapper to make it harder to confuse the packed and persistent index when used as arguments etc.
	 */
	struct FPersistentPrimitiveIndex
	{
		bool IsValid() const { return Index != INDEX_NONE; }
		int32 Index = INDEX_NONE;

		FORCEINLINE bool operator == (FPersistentPrimitiveIndex B) const
		{
			return Index == B.Index;
		}
	};
	/**
	 * Container for primtive ID info that needs to be passed around, in the future will likely be condensed to just the instance ID.
	 */
	struct FMeshDrawCommandPrimitiveIdInfo
	{
		FORCEINLINE FMeshDrawCommandPrimitiveIdInfo() {};

		// Use this ctor for scene primitves
		FORCEINLINE FMeshDrawCommandPrimitiveIdInfo(int32 InScenePrimitiveId, FPersistentPrimitiveIndex InDrawPrimitiveId, int32 InInstanceSceneDataOffset) :
			DrawPrimitiveId(InDrawPrimitiveId.Index),
			ScenePrimitiveId(InScenePrimitiveId),
			InstanceSceneDataOffset(InInstanceSceneDataOffset),
			bIsDynamicPrimitive(0)
		{
		}
		
		// Draw PrimitiveId this draw command is associated with - used by the shader to fetch primitive data from the PrimitiveSceneData SRV.
		// If it's < Scene->Primitives.Num() then it's a valid Scene PrimitiveIndex and can be used to backtrack to the FPrimitiveSceneInfo.
		int32 DrawPrimitiveId;

		// Scene PrimitiveId that generated this draw command, or -1 if no FPrimitiveSceneInfo. Can be used to backtrack to the FPrimitiveSceneInfo.
		int32 ScenePrimitiveId;

		// Offset to the first instance belonging to the primitive in GPU scene
		int32 InstanceSceneDataOffset : 31;
	
		// Set to true if the primitive ID and instance data offset is a dynamic ID, which means it needs to be translated before use.
		uint32 bIsDynamicPrimitive : 1;
	};

	struct FMeshProcessorShaders
	{
		CGIShader* mCGIVertexShader;
		CGIShader* mCGIPixelShader;
		CGIShader* mCGIGeometryShader;
		CGIShader* mCGIComputeShader;
		CGIShader* mCGIWorkGraphShader;
#if RHI_RAYTRACING
		TShaderRef<Shader> RayTracingShader;
#endif

		CGIShader* GetShader(EShaderFrequency Frequency) const
		{
			if (Frequency == SF_Vertex)
			{
				return mCGIVertexShader;
			}
			if (Frequency == SF_Pixel)
			{
				return mCGIPixelShader;
			}
			if (Frequency == SF_Geometry)
			{
				return mCGIGeometryShader;
			}
			if (Frequency == SF_Compute)
			{
				return mCGIComputeShader;
			}
			if (Frequency == SF_WorkGraphComputeNode)
			{
				return mCGIWorkGraphShader;
			}
#if RHI_RAYTRACING
			if (Frequency == SF_RayHitGroup || Frequency == SF_RayCallable || Frequency == SF_RayMiss)
			{
				if (RayTracingShader.IsValid() && Frequency != RayTracingShader->GetFrequency())
				{
					checkf(0, TEXT("Requested raytracing shader frequency (%d) doesn't match assigned shader frequency (%d)."), Frequency, RayTracingShader->GetFrequency());
					return TShaderRef<FShader>();
				}
				return RayTracingShader;
			}
#endif // RHI_RAYTRACING

			checkf(0, TEXT("Unhandled shader frequency"));
			return nullptr;
		}
	};
	enum class EMeshPassFeatures
	{
		Default = 0,
		PositionOnly = 1 << 0,
		PositionAndNormalOnly = 1 << 1,
	};
	ENUM_CLASS_FLAGS(EMeshPassFeatures);
	/**
	 * A set of render state overrides passed into a Mesh Pass Processor, so it can be configured from the outside.
	 */
	struct FMeshPassProcessorRenderState
	{
		FMeshPassProcessorRenderState() = default;
		FMeshPassProcessorRenderState(const FMeshPassProcessorRenderState& DrawRenderState) = default;
		~FMeshPassProcessorRenderState() = default;

	public:
		void SetBlendState(CGIBlendState* InBlendState)
		{
			BlendState = InBlendState;
		}

		CGIBlendState* GetBlendState() const
		{
			return BlendState;
		}

		void SetDepthStencilState(CGIDepthStencilState* InDepthStencilState)
		{
			DepthStencilState = InDepthStencilState;
			StencilRef = 0;
		}

		void SetStencilRef(uint32 InStencilRef)
		{
			StencilRef = InStencilRef;
		}

		CGIDepthStencilState* GetDepthStencilState() const
		{
			return DepthStencilState;
		}

		void SetDepthStencilAccess(ExclusiveDepthStencil::Type InDepthStencilAccess)
		{
			DepthStencilAccess = InDepthStencilAccess;
		}

		ExclusiveDepthStencil::Type GetDepthStencilAccess() const
		{
			return DepthStencilAccess;
		}

		uint32 GetStencilRef() const
		{
			return StencilRef;
		}

		void ApplyToPSO(FGraphicsPipelineStateInitializer& GraphicsPSOInit) const
		{
			GraphicsPSOInit.mCGIBlendState = BlendState;
			GraphicsPSOInit.DepthStencilState = DepthStencilState;
		}

	private:
		CGIBlendState*					BlendState =			nullptr;
		CGIDepthStencilState*			DepthStencilState =		nullptr;
		ExclusiveDepthStencil::Type	DepthStencilAccess	=	ExclusiveDepthStencil::DepthRead_StencilRead;;

		//FRHIUniformBuffer*				ViewUniformBuffer = nullptr;
		//FRHIUniformBuffer*				InstancedViewUniformBuffer = nullptr;
//
		//FRHIUniformBuffer*				PassUniformBuffer = nullptr;
		//FRHIUniformBuffer*				NaniteUniformBuffer = nullptr;

		uint32							StencilRef = 0;
	};

	enum class EDrawingPolicyOverrideFlags
	{
		None = 0,
		TwoSided = 1 << 0,
		DitheredLODTransition = 1 << 1,
		Wireframe = 1 << 2,
		ReverseCullMode = 1 << 3,
	};
	ENUM_CLASS_FLAGS(EDrawingPolicyOverrideFlags);
	struct FMinimalBoundShaderStateInput
	{
		inline FMinimalBoundShaderStateInput() {}

		FBoundShaderStateInput AsBoundShaderState() const
		{
			bool bLocalAllShaderAreLoaded = true;
			bool bCanSkipShader = AllowSkipUnloadedShaders();

			auto GetShaderResource = [&bLocalAllShaderAreLoaded](CGIShader* Shader) -> CGIShader* {
				if (Shader == nullptr)
				{
					bLocalAllShaderAreLoaded &= false;
				}
				return Shader;
			};

			if (!mCachedCGIVertexShader || !bAllShaderAreLoaded)
			{
				mCachedCGIPixelShader = mPixelShaderResource ? static_cast<CGIPixelShader*>(GetShaderResource(mPixelShaderResource->GetShader(mPixelShaderIndex, !bCanSkipShader))) : nullptr;
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
				mCachedCGIGeometryShader = mGeometryShaderResource ? static_cast<CGIGeometryShader*>(mGeometryShaderResource->GetShader(mGeometryShaderIndex)) : nullptr;
	#endif
	#if PLATFORM_SUPPORTS_MESH_SHADERS
				mCachedCGIMeshShader = mMeshShaderResource ? static_cast<CGIMeshShader*>(mMeshShaderResource->GetShader(mMeshShaderIndex)) : nullptr;
	#endif
				mCachedCGIVertexShader = mVertexShaderResource ? static_cast<CGIVertexShader*>(GetShaderResource(mVertexShaderResource->GetShader(mVertexShaderIndex, !bCanSkipShader))) : nullptr;

				bAllShaderAreLoaded = bLocalAllShaderAreLoaded;
			}

	#if PLATFORM_SUPPORTS_MESH_SHADERS
			if (CachedMeshShader)
			{
				return FBoundShaderStateInput(CachedMeshShader, nullptr, CachedPixelShader);
			}
			else
	#endif
			{
				return FBoundShaderStateInput(mCGIVertexDeclaration
					, mCachedCGIVertexShader
					, mCachedCGIPixelShader
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
					, CachedGeometryShader
	#endif
				);
			}
		}

		bool AllowSkipUnloadedShaders() const;

		bool IsShaderAllLoaded() const { return bAllShaderAreLoaded; }

		void ForceShaderReload() const 
		{
			if (!mCachedCGIVertexShader || !bAllShaderAreLoaded)
			{
				mCachedCGIPixelShader = mPixelShaderResource ? static_cast<CGIPixelShader*>(mPixelShaderResource->GetShader(mPixelShaderIndex)) : nullptr;
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
				mCachedCGIGeometryShader = GeometryShaderResource ? static_cast<FRHIGeometryShader*>(GeometryShaderResource->GetShader(GeometryShaderIndex)) : nullptr;
	#endif
	#if PLATFORM_SUPPORTS_MESH_SHADERS
				mCachedCGIMeshShader = MeshShaderResource ? static_cast<FRHIMeshShader*>(MeshShaderResource->GetShader(MeshShaderIndex)) : nullptr;
	#endif
				mCachedCGIVertexShader = mVertexShaderResource ? static_cast<CGIVertexShader*>(mVertexShaderResource->GetShader(mVertexShaderIndex)) : nullptr;

				bAllShaderAreLoaded = true;
			}
		}

		void LazilyInitShaders() const
		{
			AsBoundShaderState(); // querying shaders will initialize on demand
		}

		bool NeedsShaderInitialisation() const
		{
			if (mVertexShaderResource && !mVertexShaderResource->HasShader(mVertexShaderIndex))
			{
				return true;
			}
			if (mPixelShaderResource && !mPixelShaderResource->HasShader(mPixelShaderIndex))
			{
				return true;
			}
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
			if (GeometryShaderResource && !GeometryShaderResource->HasShader(GeometryShaderIndex))
			{
				return true;
			}
	#endif
	#if PLATFORM_SUPPORTS_MESH_SHADERS
			if (MeshShaderResource && !MeshShaderResource->HasShader(MeshShaderIndex))
			{
				return true;
			}
	#endif
			return false;
		}

		CGIVertexDeclaration* mCGIVertexDeclaration = nullptr;
		mutable CGIShader* mCachedCGIVertexShader = nullptr;
		mutable CGIShader* mCachedCGIPixelShader = nullptr;
		mutable bool bAllShaderAreLoaded = true;
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
		mutable FRHIGeometryShader* mCachedGeometryShader = nullptr;
	#endif
	#if PLATFORM_SUPPORTS_MESH_SHADERS
		mutable FRHIMeshShader* mCachedMeshShader = nullptr;
	#endif
		FShaderMapResource* mVertexShaderResource;
		FShaderMapResource* mPixelShaderResource;
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
		TRefCountPtr<FShaderMapResource> GeometryShaderResource;
	#endif
	#if PLATFORM_SUPPORTS_MESH_SHADERS
		TRefCountPtr<FShaderMapResource> MeshShaderResource;
	#endif
		int32 mVertexShaderIndex = INDEX_NONE;
		int32 mPixelShaderIndex = INDEX_NONE;
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
		int32 mGeometryShaderIndex = INDEX_NONE;
	#endif
	#if PLATFORM_SUPPORTS_MESH_SHADERS
		int32 mMeshShaderIndex = INDEX_NONE;
	#endif
	};


	/**
	 * Pipeline state without render target state
	 * Useful for mesh passes where the render target state is not changing between draws.
	 * Note: the size of this class affects rendering mesh pass traversal performance.
	 */
	class FGraphicsMinimalPipelineStateInitializer
	{
	public:

		FGraphicsMinimalPipelineStateInitializer()
			: BlendState(nullptr)
			, RasterizerState(nullptr)
			, DepthStencilState(nullptr)
			, PrimitiveType(PT_Num)
		{}

		FGraphicsMinimalPipelineStateInitializer(
			FMinimalBoundShaderStateInput	InBoundShaderState,
			CGIBlendState*					InBlendState,
			CGIRasterizerState*				InRasterizerState,
			CGIDepthStencilState*			InDepthStencilState,
			FImmutableSamplerState			InImmutableSamplerState,
			EPrimitiveType					InPrimitiveType
		)
			: BoundShaderState(InBoundShaderState)
			, BlendState(InBlendState)
			, RasterizerState(InRasterizerState)
			, DepthStencilState(InDepthStencilState)
			, ImmutableSamplerState(InImmutableSamplerState)
			, PrimitiveType(InPrimitiveType)
		{
		}

		FGraphicsMinimalPipelineStateInitializer(const FGraphicsMinimalPipelineStateInitializer& InMinimalState)
			: BoundShaderState(InMinimalState.BoundShaderState)
			, BlendState(InMinimalState.BlendState)
			, RasterizerState(InMinimalState.RasterizerState)
			, DepthStencilState(InMinimalState.DepthStencilState)
			, ImmutableSamplerState(InMinimalState.ImmutableSamplerState)
			, bDepthBounds(InMinimalState.bDepthBounds)
			, bAllowVariableRateShading(InMinimalState.bAllowVariableRateShading)
			, DrawShadingRate(InMinimalState.DrawShadingRate)
			, PrimitiveType(InMinimalState.PrimitiveType)
			, StatePrecachePSOHash(InMinimalState.StatePrecachePSOHash)
			, PSOPrecacheState(InMinimalState.PSOPrecacheState)
		{
		}

		void SetupBoundShaderState(CGIVertexDeclaration* VertexDeclaration, const FMeshProcessorShaders& Shaders);

 		void ComputeStatePrecachePSOHash();

		FGraphicsPipelineStateInitializer AsGraphicsPipelineStateInitializer() const
		{	
			FGraphicsPipelineStateInitializer result(
				BoundShaderState.AsBoundShaderState()
				, BlendState
				, RasterizerState
				, DepthStencilState
				, ImmutableSamplerState
				, PrimitiveType
				, 0
				, std::vector<EPixelFormat>(InPlace,EPixelFormat::PF_Unknown)
				, std::vector<ETextureCreateFlags>(InPlace,ETextureCreateFlags::None)
				, EPixelFormat::PF_Unknown
				, ETextureCreateFlags::None
				, ERenderTargetLoadAction::ENoAction
				, ERenderTargetStoreAction::ENoAction
				, ERenderTargetLoadAction::ENoAction
				, ERenderTargetStoreAction::ENoAction
				, ExclusiveDepthStencil::DepthNop
				, 0
				, ESubpassHint::None
				, 0
				, EConservativeRasterization::Disabled
				, 0
				, bDepthBounds
				, 0
				, false
				, bAllowVariableRateShading
				, DrawShadingRate
			);

			if (PipelineStateCache::IsPSOPrecachingEnabled() )
			{
				checkSlow(StatePrecachePSOHash == 0 || RHIComputeStatePrecachePSOHash(result) == StatePrecachePSOHash);
				//result.StatePrecachePSOHash = StatePrecachePSOHash == 0 ? RHIComputeStatePrecachePSOHash(result) : StatePrecachePSOHash;			
			}

			return result;
		}

		inline bool operator==(const FGraphicsMinimalPipelineStateInitializer& rhs) const
		{
			if (BoundShaderState.mCGIVertexDeclaration != rhs.BoundShaderState.mCGIVertexDeclaration ||
				BoundShaderState.mVertexShaderResource != rhs.BoundShaderState.mVertexShaderResource ||
				BoundShaderState.mPixelShaderResource != rhs.BoundShaderState.mPixelShaderResource ||
				BoundShaderState.mVertexShaderIndex != rhs.BoundShaderState.mVertexShaderIndex ||
				BoundShaderState.mPixelShaderIndex != rhs.BoundShaderState.mPixelShaderIndex ||
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
				BoundShaderState.GeometryShaderResource != rhs.BoundShaderState.GeometryShaderResource ||
				BoundShaderState.GeometryShaderIndex != rhs.BoundShaderState.GeometryShaderIndex ||
	#endif
				BlendState != rhs.BlendState ||
				RasterizerState != rhs.RasterizerState ||
				DepthStencilState != rhs.DepthStencilState ||
				ImmutableSamplerState != rhs.ImmutableSamplerState ||
				bDepthBounds != rhs.bDepthBounds ||
				bAllowVariableRateShading != rhs.bAllowVariableRateShading ||
				DrawShadingRate != rhs.DrawShadingRate ||
				PrimitiveType != rhs.PrimitiveType)
			{
				return false;
			}

			return true;
		}

		inline bool operator!=(const FGraphicsMinimalPipelineStateInitializer& rhs) const
		{
			return !(*this == rhs);
		}

		inline friend uint32 GetTypeHash(const FGraphicsMinimalPipelineStateInitializer& Initializer)
		{
			//add and initialize any leftover padding within the struct to avoid unstable key
			struct FHashKey
			{
				uint32 VertexDeclaration;
				uint32 VertexShader;
				uint32 PixelShader;
				uint32 RasterizerState;
			} HashKey;
			HashKey.VertexDeclaration = PointerHash(Initializer.BoundShaderState.mCGIVertexDeclaration);
			//HashKey.VertexShader = GetTypeHash<int32>(Initializer.BoundShaderState.VertexShaderIndex);
			//HashKey.PixelShader = GetTypeHash<int32>(Initializer.BoundShaderState.PixelShaderIndex);
			HashKey.RasterizerState = PointerHash(Initializer.RasterizerState);

			return uint32(CityHash64((const char*)&HashKey, sizeof(FHashKey)));
		}

	#define COMPARE_FIELD_BEGIN(Field) \
			if (Field != rhs.Field) \
			{ return Field COMPARE_OP rhs.Field; }

	#define COMPARE_FIELD(Field) \
			else if (Field != rhs.Field) \
			{ return Field COMPARE_OP rhs.Field; }

	#define COMPARE_FIELD_END \
			else { return false; }

		bool operator<(const FGraphicsMinimalPipelineStateInitializer& rhs) const
		{
	#define COMPARE_OP <

			COMPARE_FIELD_BEGIN(BoundShaderState.mCGIVertexDeclaration)
				COMPARE_FIELD(BoundShaderState.mVertexShaderIndex)
				COMPARE_FIELD(BoundShaderState.mPixelShaderIndex)
				COMPARE_FIELD(BoundShaderState.mVertexShaderResource)
				COMPARE_FIELD(BoundShaderState.mPixelShaderResource)
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
				COMPARE_FIELD(BoundShaderState.GeometryShaderIndex)
				COMPARE_FIELD(BoundShaderState.GeometryShaderResource)
	#endif
				COMPARE_FIELD(BlendState)
				COMPARE_FIELD(RasterizerState)
				COMPARE_FIELD(DepthStencilState)
				COMPARE_FIELD(bDepthBounds)
				COMPARE_FIELD(bAllowVariableRateShading)
				COMPARE_FIELD(DrawShadingRate)
				COMPARE_FIELD(PrimitiveType)
			COMPARE_FIELD_END;

	#undef COMPARE_OP
		}

		bool operator>(const FGraphicsMinimalPipelineStateInitializer& rhs) const
		{
	#define COMPARE_OP >

			COMPARE_FIELD_BEGIN(BoundShaderState.mCGIVertexDeclaration)
				COMPARE_FIELD(BoundShaderState.mVertexShaderIndex)
				COMPARE_FIELD(BoundShaderState.mPixelShaderIndex)
				COMPARE_FIELD(BoundShaderState.mVertexShaderResource)
				COMPARE_FIELD(BoundShaderState.mPixelShaderResource)
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
				COMPARE_FIELD(BoundShaderState.GeometryShaderIndex)
				COMPARE_FIELD(BoundShaderState.GeometryShaderResource)
	#endif
				COMPARE_FIELD(BlendState)
				COMPARE_FIELD(RasterizerState)
				COMPARE_FIELD(DepthStencilState)
				COMPARE_FIELD(bDepthBounds)
				COMPARE_FIELD(bAllowVariableRateShading)
				COMPARE_FIELD(DrawShadingRate)
				COMPARE_FIELD(PrimitiveType)
				COMPARE_FIELD_END;

	#undef COMPARE_OP
		}

	#undef COMPARE_FIELD_BEGIN
	#undef COMPARE_FIELD
	#undef COMPARE_FIELD_END

		// TODO: [PSO API] - As we migrate reuse existing API objects, but eventually we can move to the direct initializers. 
		// When we do that work, move this to RHI.h as its more appropriate there, but here for now since dependent typdefs are here.
		FMinimalBoundShaderStateInput	BoundShaderState;
		CGIBlendState*					BlendState;
		CGIRasterizerState*				RasterizerState;
		CGIDepthStencilState*			DepthStencilState;
		FImmutableSamplerState			ImmutableSamplerState;

		// Note: FGraphicsMinimalPipelineStateInitializer is 8-byte aligned and can't have any implicit padding,
		// as it is sometimes hashed and compared as raw bytes. Explicit padding is therefore required between
		// all data members and at the end of the structure.
		bool							bDepthBounds = false;
		bool							bAllowVariableRateShading = true;
		EVRSShadingRate					DrawShadingRate  = EVRSShadingRate::VRSSR_1x1;

		EPrimitiveType					PrimitiveType;
			
		// Data hash of the minimal PSO which is used to optimize the computation of the full PSO
		uint64							StatePrecachePSOHash = 0;
		// The PSO precache state - updated at draw time and can be used to skip draw when still precaching
		mutable EPSOPrecacheResult		PSOPrecacheState = EPSOPrecacheResult::Unknown;
	};
	/**
	 * Interface class implemented by the mesh pass processor to collect all possible PSOs
	 */
	class IPSOCollector
	{
	public:
		// PSO Collector index used for stats tracking
		int32 PSOCollectorIndex = INDEX_NONE;
		IPSOCollector(int32 InPSOCollectorIndex) : PSOCollectorIndex(InPSOCollectorIndex) {}
	};
	class FMeshDrawShaderBindingsLayout
	{
	public:
	};
	class FMeshDrawSingleShaderBindings : public FMeshDrawShaderBindingsLayout
	{
	public:
	};
	class FReadOnlyMeshDrawSingleShaderBindings : public FMeshDrawShaderBindingsLayout
	{
	public:
	};
	/**
	 * Encapsulates shader bindings for a single FMeshDrawCommand.
	 */
	class FMeshDrawShaderBindings
	{
	public:
		std::vector<FMeshDrawShaderBindingsLayout> ShaderLayouts;
		struct FData
		{
			uint8* InlineStorage[NumInlineShaderBindings] = {};
			uint8* GetHeapData()
			{
				return InlineStorage[0];
			}
			const uint8* GetHeapData() const
			{
				return InlineStorage[0];
			}
			void SetHeapData(uint8* HeapData)
			{
				InlineStorage[0] = HeapData;
			}
		} Data = {};
		uint16 ShaderFrequencyBits = 0;
		uint16 Size = 0;
		FMeshDrawShaderBindings(){}
		void Initialize(const FMeshProcessorShaders& Shaders);
	};
	class FMeshDrawCommandStateCache
	{
	public:

		uint32 PipelineId;
		uint32 StencilRef;
	};
	struct FMeshDrawCommandSceneArgs
	{
	};
	/** Uniquely represents a FGraphicsMinimalPipelineStateInitializer for fast compares. */
	class FGraphicsMinimalPipelineStateId
	{
	public:
		union
		{
			uint32 PackedId = 0;

			struct
			{
				uint32 SetElementIndex				   : 30;
				uint32 bComesFromLocalPipelineStateSet : 1;
				uint32 bValid						   : 1;
			};
		};
		/**
		 * Get a ref counted persistent pipeline id, which needs to manually released.
		 */
		static FGraphicsMinimalPipelineStateId GetPersistentId(const FGraphicsMinimalPipelineStateInitializer& InPipelineState);
	};
	/** 
	 * FMeshDrawCommand fully describes a mesh pass draw call, captured just above the RHI.  
			FMeshDrawCommand should contain only data needed to draw.  For InitViews payloads, use FVisibleMeshDrawCommand.
	 * FMeshDrawCommands are cached at Primitive AddToScene time for vertex factories that support it (no per-frame or per-view shader binding changes).
	 * Dynamic Instancing operates at the FMeshDrawCommand level for robustness.  
			Adding per-command shader bindings will reduce the efficiency of Dynamic Instancing, but rendering will always be correct.
	 * Any resources referenced by a command must be kept alive for the lifetime of the command.  FMeshDrawCommand is not responsible for lifetime management of resources.
			For uniform buffers referenced by cached FMeshDrawCommand's, RHIUpdateUniformBuffer makes it possible to access per-frame data in the shader without changing bindings.
	 */
	class FMeshDrawCommand
	{
	public:
		/**
		 * Resource bindings
		 */
		FMeshDrawShaderBindings ShaderBindings;
		std::array<VertexInputStream,8> mVertexInputStreams;
		/**
		 * PSO
		 */
		FGraphicsMinimalPipelineStateId CachedPipelineId;
		/**
		 * Draw command parameters
		 */
		uint32 FirstIndex;
		uint32 NumPrimitives;
		uint32 NumInstances;
		union
		{
			struct 
			{
				uint32 BaseVertexIndex;
				uint32 NumVertices;
			} VertexParams;
		
			struct  
			{
				CGIBuffer* Buffer;
				uint32 Offset;
			} IndirectArgs;
		};
		FMeshDrawCommand(){}
		FMeshDrawCommand(FMeshDrawCommand*inMeshDrawCommand){}
		/** Called when the mesh draw command is complete. */
		void SetDrawParametersAndFinalize(
			const FMeshBatch& MeshBatch, 
			int32 BatchElementIndex,
			FGraphicsMinimalPipelineStateId PipelineId,
			const FMeshProcessorShaders* ShadersForDebugging);
		void Finalize(FGraphicsMinimalPipelineStateId PipelineId, const FMeshProcessorShaders* ShadersForDebugging)
		{
			CachedPipelineId = PipelineId;
			//ShaderBindings.Finalize(ShadersForDebugging);	
		}
		/** 
		 * Submits the state and shader bindings to the RHI command list, but does not invoke the draw. 	 
		 * If function returns false, then draw can be skipped because the PSO used by the draw command is not compiled yet (still precaching)
		 */
		static bool SubmitDrawBegin(
			const FMeshDrawCommand& MeshDrawCommand,
			const std::unordered_set<FGraphicsMinimalPipelineStateInitializer*>& GraphicsMinimalPipelineStateSet,
			const FMeshDrawCommandSceneArgs& SceneArgs,
			uint32 InstanceFactor,
			CGICommandList& RHICmdList,
			FMeshDrawCommandStateCache& StateCache,
			bool bAllowSkipDrawCommand);

		/** Submits just the draw primitive portion of the draw command. */
		static void SubmitDrawEnd(const FMeshDrawCommand& MeshDrawCommand, const FMeshDrawCommandSceneArgs& SceneArgs, uint32 InstanceFactor, CGICommandList& RHICmdList);
		/** Submits commands to the RHI Commandlist to draw the MeshDrawCommand. */
		static bool SubmitDraw(
			const FMeshDrawCommand& MeshDrawCommand,
			const std::unordered_set<FGraphicsMinimalPipelineStateInitializer*>& GraphicsMinimalPipelineStateSet,
			const FMeshDrawCommandSceneArgs& SceneArgs,
			uint32 InstanceFactor,
			CGICommandList& CommandList,
			class FMeshDrawCommandStateCache& StateCache);
	};
	/** 
	 * Stores information about a mesh draw command that has been determined to be visible, for further visibility processing. 
	 * This class should only store data needed by InitViews operations (visibility, sorting) and not data needed for draw submission, which belongs in FMeshDrawCommand.
	 */
	class FVisibleMeshDrawCommand
	{
	public:
		// Mesh Draw Command stored separately to avoid fetching its data during sorting
		const FMeshDrawCommand* mMeshDrawCommand;
		void Setup(
			const FMeshDrawCommand* InMeshDrawCommand,
			const FMeshDrawCommandPrimitiveIdInfo& InPrimitiveIdInfo,
			int32 InStateBucketId,
			ERasterizerFillMode InMeshFillMode,
			ERasterizerCullMode InMeshCullMode,
			EFVisibleMeshDrawCommandFlags InFlags,
			FMeshDrawCommandSortKey InSortKey,
			//FMeshDrawCommandCullingPayload InCullingPayload,
			//EMeshDrawCommandCullingPayloadFlags InCullingPayloadFlags,
			const uint32* InRunArray = nullptr,
			int32 InNumRuns = 0)
		{
			//MeshDrawCommand = InMeshDrawCommand;
			//PrimitiveIdInfo = InPrimitiveIdInfo;
			//PrimitiveIdBufferOffset = -1;
			//StateBucketId = InStateBucketId;
			//MeshFillMode = InMeshFillMode;
			//MeshCullMode = InMeshCullMode;
			//SortKey = InSortKey;
			//Flags = InFlags;
			//CullingPayload = InCullingPayload;
			//CullingPayloadFlags = InCullingPayloadFlags;
			//RunArray = InRunArray;
			//NumRuns = InNumRuns;
		}
	};
	/** Interface for the different types of draw lists. */
	class FMeshPassDrawListContext
	{
	public:
		virtual ~FMeshPassDrawListContext() {}
		virtual FMeshDrawCommand& AddCommand(FMeshDrawCommand& Initializer, uint32 NumElements) = 0;

		virtual void FinalizeCommand(
			const FMeshBatch& MeshBatch, 
			int32 BatchElementIndex,
			const FMeshDrawCommandPrimitiveIdInfo& IdInfo,
			ERasterizerFillMode MeshFillMode,
			ERasterizerCullMode MeshCullMode,
			FMeshDrawCommandSortKey SortKey,
			EFVisibleMeshDrawCommandFlags Flags,
			const FGraphicsMinimalPipelineStateInitializer& PipelineState,
			const FMeshProcessorShaders* ShadersForDebugging,
			FMeshDrawCommand& MeshDrawCommand) = 0;
	};
	/**
	 * Container for mesh command data required by instance culling.
	 */
	struct FMeshDrawCommandCullingPayload
	{
		union
		{
			uint32 PackedData = 0;
			struct
			{
				uint32 LodIndex : 4;
				uint32 MinScreenSize : 12;
				uint32 MaxScreenSize : 12;
			};
		};

		// Pack screen sizes into fixed point 3.9
		static uint32 PackScreenSize(float ScreenSize)
		{
			constexpr float MaxExpressibleValue = (float)(0xFFFU) / (float)(1 << 9);
			ScreenSize = MathUtils::Clamp(ScreenSize, 0.f, MaxExpressibleValue);
			return (uint32)(ScreenSize * (1 << 9)) & 0xFFF;
		}

		static float UnpackScreenSize(uint32 PackedScreenSize)
		{
			return (float)(PackedScreenSize & 0xFFFU) / (float)(1 << 9);
		}
	};

	static FMeshDrawCommandCullingPayload CreateCullingPayload(FMeshBatch const& MeshBatch, FMeshBatchElement const& MeshBatchElement)
	{
		FMeshDrawCommandCullingPayload CullingPayload;
		CullingPayload.LodIndex = MeshBatch.LODIndex;
		CullingPayload.MinScreenSize = FMeshDrawCommandCullingPayload::PackScreenSize(MeshBatchElement.MinScreenSize);
		CullingPayload.MaxScreenSize = FMeshDrawCommandCullingPayload::PackScreenSize(MeshBatchElement.MaxScreenSize);
		return CullingPayload;
	}

	/**
	 * Flags stored on visible mesh commands to indicate how to interpret the culling payload.
	 */
	enum class EMeshDrawCommandCullingPayloadFlags : uint8
	{
		Default = 0U,
		MinScreenSizeCull = 1U, // Cull when below a minimum screen space size.
		MaxScreenSizeCull = 2U, // Cull when above a maximum screen space size.
	
		NoScreenSizeCull = Default,
		All = MinScreenSizeCull | MaxScreenSizeCull,
		NumBits = 2U
	};
	ENUM_CLASS_FLAGS(EMeshDrawCommandCullingPayloadFlags);
	static_assert(uint32(EMeshDrawCommandCullingPayloadFlags::All) < (1U << uint32(EMeshDrawCommandCullingPayloadFlags::NumBits)), "EMeshDrawCommandCullingPayloadFlags::NumBits too small to represent all flags in EMeshDrawCommandCullingPayloadFlags.");


	/** 
	 * Stores information about a mesh draw command which is cached in the scene. 
	 * This is stored separately from the cached FMeshDrawCommand so that InitViews does not have to load the FMeshDrawCommand into cache.
	 */
	class FCachedMeshDrawCommandInfo
	{
	public:
		FCachedMeshDrawCommandInfo() : FCachedMeshDrawCommandInfo(EMeshPass::Num)
		{}

		explicit FCachedMeshDrawCommandInfo(EMeshPass::Type InMeshPass) :
			SortKey(FMeshDrawCommandSortKey::Default),
			CommandIndex(INDEX_NONE),
			StateBucketId(INDEX_NONE),
			MeshPass(InMeshPass),
			MeshFillMode(ERasterizerFillMode_Num),
			MeshCullMode(ERasterizerCullMode_Num),
			Flags(EFVisibleMeshDrawCommandFlags::Default)
		{}

		/** Starting offset into continuous array of command infos for this mesh in FPrimitiveSceneInfo::CachedMeshDrawCommandInfos. */
		FMeshPassMask CommandInfosMask;
		
		FMeshDrawCommandSortKey SortKey;

		// Stores the index into FScene::CachedDrawLists of the corresponding FMeshDrawCommand, or -1 if not stored there
		int32 CommandIndex;

		// Stores the index into FScene::CachedMeshDrawCommandStateBuckets of the corresponding FMeshDrawCommand, or -1 if not stored there
		int32 StateBucketId;

		// Used for passing LOD info to the culling
		FMeshDrawCommandCullingPayload CullingPayload;

		// Needed for easier debugging and faster removal of cached mesh draw commands.
		EMeshPass::Type MeshPass : EMeshPass::NumBits + 1;

		// Needed for view overrides
		ERasterizerFillMode MeshFillMode : ERasterizerFillMode_NumBits + 1;
		ERasterizerCullMode MeshCullMode : ERasterizerCullMode_NumBits + 1;

		EFVisibleMeshDrawCommandFlags Flags : uint32(EFVisibleMeshDrawCommandFlags::NumBits);
	};
	class FCachedPassMeshDrawList
	{
	public:

		FCachedPassMeshDrawList() :
			LowestFreeIndexSearchStart(0)
		{}

		/** Indices held by FStaticMeshBatch::CachedMeshDrawCommands must be stable */
		std::vector<FMeshDrawCommand> MeshDrawCommands;
		int32 LowestFreeIndexSearchStart;
	};
	class FCachedPassMeshDrawListContext : public FMeshPassDrawListContext
	{
	public:
		struct FMeshPassScope
		{
			FMeshPassScope(const FMeshPassScope&) = delete;
			FMeshPassScope& operator=(const FMeshPassScope&) = delete;

			inline FMeshPassScope(FCachedPassMeshDrawListContext& InContext, EMeshPass::Type MeshPass)
				: Context(InContext)
			{
				Context.BeginMeshPass(MeshPass);
			}

			inline ~FMeshPassScope()
			{
				Context.EndMeshPass();
			}
		
		private:
			FCachedPassMeshDrawListContext& Context;
		};

		FCachedPassMeshDrawListContext(Scene& inScene):mScene(inScene),bUseGPUScene(false){}

		virtual FMeshDrawCommand& AddCommand(FMeshDrawCommand& Initializer, uint32 NumElements) override final;

		void BeginMeshPass(EMeshPass::Type MeshPass);
		void EndMeshPass();

		void BeginMesh(int32 SceneInfoIndex, int32 MeshIndex);
		void EndMesh();

		FCachedMeshDrawCommandInfo GetCommandInfoAndReset();
		bool HasAnyLooseParameterBuffers() const { return bAnyLooseParameterBuffers; }	

	protected:
		void FinalizeCommandCommon(
			const FMeshBatch& MeshBatch, 
			int32 BatchElementIndex,
			ERasterizerFillMode MeshFillMode,
			ERasterizerCullMode MeshCullMode,
			FMeshDrawCommandSortKey SortKey,
			EFVisibleMeshDrawCommandFlags Flags,
			const FGraphicsMinimalPipelineStateInitializer& PipelineState,
			const FMeshProcessorShaders* ShadersForDebugging,
			FMeshDrawCommand& MeshDrawCommand);
		
		Scene& mScene;
		FMeshDrawCommand MeshDrawCommandForStateBucketing;
		FCachedMeshDrawCommandInfo CommandInfo;
		EMeshPass::Type CurrMeshPass = EMeshPass::Num;
		bool bUseGPUScene = false;
		bool bAnyLooseParameterBuffers = false;
	};
	class FCachedPassMeshDrawListContextImmediate : public FCachedPassMeshDrawListContext
	{
	public:
		FCachedPassMeshDrawListContextImmediate(Scene& InScene) : FCachedPassMeshDrawListContext(InScene) {}

		virtual void FinalizeCommand(
			const FMeshBatch& MeshBatch, 
			int32 BatchElementIndex,
			const FMeshDrawCommandPrimitiveIdInfo& IdInfo,
			ERasterizerFillMode MeshFillMode,
			ERasterizerCullMode MeshCullMode,
			FMeshDrawCommandSortKey SortKey,
			EFVisibleMeshDrawCommandFlags Flags,
			const FGraphicsMinimalPipelineStateInitializer& PipelineState,
			const FMeshProcessorShaders* ShadersForDebugging,
			FMeshDrawCommand& MeshDrawCommand) override final;
	};
	class SceneView;
	/** 
	 * Base class of mesh processors, whose job is to transform FMeshBatch draw descriptions received from scene proxy implementations into FMeshDrawCommands ready for the RHI command list
	 */
	class FMeshPassProcessor : public IPSOCollector
	{
	public:
		EMeshPass::Type MeshPassType;
		const Scene* mScene;
		const SceneView* ViewIfDynamicMeshCommand;
		FMeshPassDrawListContext* DrawListContext;
		FMeshPassProcessor(EMeshPass::Type InMeshPassType, const Scene* InScene, const SceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext);
		virtual ~FMeshPassProcessor() {}

		void SetDrawListContext(FMeshPassDrawListContext* InDrawListContext)
		{
			DrawListContext = InDrawListContext;
		}
		// FMeshPassProcessor interface
		// Add a FMeshBatch to the pass
		virtual void AddMeshBatch(const FMeshBatch& MeshBatch, uint64 BatchElementMask, const PrimitiveSceneProxy* PrimitiveSceneProxy, int32 StaticMeshId = -1) = 0;
		template<typename PassShadersType, typename ShaderElementDataType>
		void BuildMeshDrawCommands(
			const FMeshBatch& MeshBatch,
			uint64 BatchElementMask,
			const PrimitiveSceneProxy* PrimitiveSceneProxy,
			const MaterialRenderProxy& MaterialRenderProxy,
			const MaterialShared& MaterialResource,
			const FMeshPassProcessorRenderState& DrawRenderState,
			const PassShadersType& PassShaders,
			ERasterizerFillMode MeshFillMode,
			ERasterizerCullMode MeshCullMode,
			FMeshDrawCommandSortKey SortKey,
			EMeshPassFeatures MeshPassFeatures,
			const ShaderElementDataType& ShaderElementData)
		{
			const VertexFactory* vertexFactory=MeshBatch.mVertexFactory;
		}
	};
	typedef FMeshPassProcessor* (*DeprecatedPassProcessorCreateFunction)(const Scene* Scene, const SceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext);
	typedef FMeshPassProcessor* (*PassProcessorCreateFunction)(const Scene* Scene, const SceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext);

	enum class EMeshPassFlags
	{
		None = 0,
		CachedMeshCommands = 1 << 0,
		MainView = 1 << 1
	};
	ENUM_CLASS_FLAGS(EMeshPassFlags);

	class FPassProcessorManager
	{
	public:
		static PassProcessorCreateFunction mJumpTable[(uint32)EShadingPath::Num][EMeshPass::Num];
		static FMeshPassProcessor* CreateMeshPassProcessor(EShadingPath ShadingPath, EMeshPass::Type PassType, const Scene* Scene, const SceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
		{
			if(mJumpTable[(uint32)ShadingPath][PassType]!=nullptr)
			{
				return mJumpTable[(uint32)ShadingPath][PassType](Scene,InViewIfDynamicMeshCommand,InDrawListContext);
			}
			return nullptr;
		}
	};
	class FRegisterPassProcessorCreateFunction
	{
	public:
		EShadingPath mShadingPath;
		EMeshPass::Type mPassType;
		FRegisterPassProcessorCreateFunction(PassProcessorCreateFunction CreateFunction, EShadingPath InShadingPath, EMeshPass::Type InPassType, EMeshPassFlags PassFlags, int32 PSOCollectorIndex = INDEX_NONE) 
		: mShadingPath(InShadingPath)
		, mPassType(InPassType)
		{
			FPassProcessorManager::mJumpTable[(uint32)InShadingPath][InPassType]=CreateFunction;
		}
	};
	// Helper marco to register the mesh pass processor to both the FPassProcessorManager & the FPSOCollectorCreateManager
	#define REGISTER_MESHPASSPROCESSOR(Name, MeshPassProcessorCreateFunction, ShadingPath, MeshPass, MeshPassFlags) \
	FRegisterPassProcessorCreateFunction RegisterMeshPassProcesser##Name(&MeshPassProcessorCreateFunction, ShadingPath, MeshPass, MeshPassFlags);
	
	#define REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(Name, MeshPassProcessorCreateFunction, ShadingPath, MeshPass, MeshPassFlags) \
	IPSOCollector* CreatePSOCollector##Name(ERHIFeatureLevel::Type FeatureLevel) \
	{ \
	return MeshPassProcessorCreateFunction(FeatureLevel, nullptr, nullptr, nullptr); \
	} \
	FRegisterPSOCollectorCreateFunction RegisterPSOCollector##Name(&CreatePSOCollector##Name, ShadingPath, GetMeshPassName(MeshPass)); \
	FRegisterPassProcessorCreateFunction RegisterMeshPassProcesser##Name(&MeshPassProcessorCreateFunction, ShadingPath, MeshPass, MeshPassFlags, RegisterPSOCollector##Name.GetIndex());

	/** 
	 * A reference to a mesh batch that is added to the collector, together with some cached relevance flags. 
	 */
	struct FMeshBatchAndRelevance
	{
		const FMeshBatch* mMesh;
		/** The render info for the primitive which created this mesh, required. */
		const PrimitiveSceneProxy* mPrimitiveSceneProxy;
		/** 
		 * Cached usage information to speed up traversal in the most costly passes (depth-only, base pass, shadow depth), 
		 * This is done so the Mesh does not have to be dereferenced to determine pass relevance. 
		 */
		uint32 bHasOpaqueMaterial : 1;
		uint32 bHasMaskedMaterial : 1;
		uint32 bRenderInMainPass : 1;
		FMeshBatchAndRelevance(const FMeshBatch& inMesh, const PrimitiveSceneProxy* inPrimitiveSceneProxy);
		bool GetHasOpaqueMaterial() const { return bHasOpaqueMaterial; }
		bool GetHasMaskedMaterial() const { return bHasMaskedMaterial; }
		bool GetHasOpaqueOrMaskedMaterial() const { return bHasOpaqueMaterial || bHasMaskedMaterial; }
		bool GetRenderInMainPass() const { return bRenderInMainPass; }
	};
	/**	
	 * Parallel mesh draw command pass setup task context.
	 */
	class FMeshDrawCommandPassSetupTaskContext
	{
	public:
		std::vector<FVisibleMeshDrawCommand*> mMeshDrawCommands;
		bool bDynamicInstancing;
		int32 InstanceFactor;
		std::unordered_set<FGraphicsMinimalPipelineStateInitializer*> mMinimalPipelineStatePassSet;
	};
	/**
	 * Parallel mesh draw command processing and rendering. 
	 * Encapsulates two parallel tasks - mesh command setup task and drawing task.
	 */
	class FParallelMeshDrawCommandPass
	{
	public:
		FMeshDrawCommandPassSetupTaskContext mMeshDrawCommandPassSetupTaskContext;
		bool bHasInstanceCullingDrawParameters;
		// Maximum number of draws for this pass. Used to prealocate resources on rendering thread. 
		// Has a guarantee that if there won't be any draws, then MaxNumDraws = 0;
		int32 MaxNumDraws;
		FParallelMeshDrawCommandPass()
			: bHasInstanceCullingDrawParameters(false)
			, MaxNumDraws(0)
		{
		}
		/**
		 * Dispatch visible mesh draw command process task, which prepares this pass for drawing.
		 * This includes generation of dynamic mesh draw commands, draw sorting and draw merging.
		 */
		void DispatchPassSetup(
			Scene* inScene,
			const ViewInfo& inViewInfo, 
			//FInstanceCullingContext &&InstanceCullingContext,
			EMeshPass::Type PassType, 
			//FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
			FMeshPassProcessor* MeshPassProcessor,
			const std::vector<FMeshBatchAndRelevance>& DynamicMeshElements,
			const std::vector<FMeshPassMask>* DynamicMeshElementsPassRelevance,
			int32 NumDynamicMeshElements,
			std::vector<const FStaticMeshBatch*>& InOutDynamicMeshCommandBuildRequests,
			std::vector<EMeshDrawCommandCullingPayloadFlags> InOutDynamicMeshCommandBuildFlags,
			int32 NumDynamicMeshCommandBuildRequestElements,
			std::vector<FVisibleMeshDrawCommand*>& InOutMeshDrawCommands,
			FMeshPassProcessor* MobileBasePassCSMMeshPassProcessor = nullptr, // Required only for the mobile base pass.
			std::vector<FVisibleMeshDrawCommand*>* InOutMobileBasePassCSMMeshDrawCommands = nullptr // Required only for the mobile base pass.
		);
		/**
		 * Dispatch visible mesh draw command draw task.
		 */
		void Draw(
			CGICommandList& RHICmdList
			//, const FInstanceCullingDrawParams* InstanceCullingDrawParams = nullptr
			) const;
	};
	extern void SubmitMeshDrawCommandsRange(
		const std::vector<FVisibleMeshDrawCommand*>& VisibleMeshDrawCommands,
		const std::unordered_set<FGraphicsMinimalPipelineStateInitializer*>& GraphicsMinimalPipelineStateSet,
		const FMeshDrawCommandSceneArgs& SceneArgs,
		uint32 PrimitiveIdBufferStride,
		bool bDynamicInstancing,
		int32 StartIndex,
		int32 NumMeshDrawCommands,
		uint32 InstanceFactor,
		CGICommandList& RHICmdList);
}
